package cn.autumnorange.app.common.rpc.anntation;

import cn.autumnorange.app.common.rpc.lock.RPCIdempotentLockTarget;
import lombok.extern.slf4j.Slf4j;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.lang3.StringUtils;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

/** 全局注解识别容器,将指定的注解对象注入会检测该类以及接口方法上是否包含该注解, 通过动态代理aop横切指定的包下的类来达到检测目的,提供策略处理类处理识别的具体注解需要实现的业务逻辑 */
@Slf4j
public class AnnotationContext implements GlobalAnnotationStrategy{
  private static volatile AnnotationContext annotationContext;
  private Map<String, AnnotationStrategy> annotationStrategyMap;

  public static AnnotationContext getAnnotationContext() {
    if (annotationContext == null) {
      synchronized (AnnotationContext.class) {
        if (annotationContext == null) {
          annotationContext = new AnnotationContext();
        }
      }
    }
    return annotationContext;
  }

  private AnnotationContext(Map<String, AnnotationStrategy> annotationStrategyMap) {
    this.annotationStrategyMap = annotationStrategyMap;
  }

  private AnnotationContext() {
    this.annotationStrategyMap = new HashMap<String, AnnotationStrategy>();
  }

  public void putAnnotationStrategy(String annotationKey, AnnotationStrategy annotationStrategy) {
    annotationStrategyMap.put(annotationKey, annotationStrategy);
  }

  public AnnotationStrategy removeAnnotationStrategy(
      String annotationKey, AnnotationStrategy annotationStrategy) {
    return annotationStrategyMap.remove(annotationKey);
  }

  /**
   * * 校验注解是否在指定的类/接口的方法上
   *
   * @param classesName
   * @param methodName
   * @param annotationName 注解类名
   * @param checkInterface 当前类/接口找不到是否从父级接口校验
   * @param checkParent 当前类/接口找不到是否从父级类校验
   * @return
   */
  public Annotation checkAnnotation(
      String classesName,
      String methodName,
      String annotationName,
      boolean checkInterface,
      boolean checkParent) throws ClassNotFoundException {
    Annotation annotationTarget = null;
    if (StringUtils.trimToEmpty(classesName).equals("")) {
      return null;
    }
    Class classes = null;
    Method[] methods = null;

      classes = Class.forName(classesName);


    methods = classes.getMethods();
    if (methods != null) {
      for (Method method : methods) {
        // 匹配注解方法是否存在指定的类方法中 匹配方式 方法名+方法参数+方法返回参数 组成字符串匹配
        String methodMsg = getMethodAllMSG(method);
        //                timestampToYYMMDDlongorg.springframework.http.ResponseEntity
        if (methodMsg.equals(methodName)) {
          //          rpcIdempotentLockTarget
          Class annotationClass = Class.forName(annotationName);
         
          annotationTarget = method.getAnnotation(annotationClass);
          if (annotationTarget != null) {
            return annotationTarget;
          }
        }
      }
    }

    if (checkInterface) {
      Class<?>[] interfaces = classes.getInterfaces();
      for (Class classInterface : interfaces) {
        annotationTarget =
            checkAnnotation(
                classInterface.getName(), methodName, annotationName, checkInterface, checkParent);
        if (annotationTarget != null) {
          return annotationTarget;
        }
      }
    }

    if (checkParent) {
      Class<?> superclass = classes.getSuperclass();
      if (superclass != null) {
        annotationTarget =
            checkAnnotation(
                superclass.getName(), methodName, annotationName, checkInterface, checkParent);
        if (annotationTarget != null) {
          return annotationTarget;
        }
      }
    }
    return annotationTarget;
  }

  public String getMethodAllMSG(Method method) {
    if (method == null) {
      return null;
    }
    String methodMsg = method.getName();
    Class<?>[] parameterTypes = method.getParameterTypes();
    String paramTypeClassStr = "";
    for (Class<?> paramTypeClass : parameterTypes) {
      paramTypeClassStr += paramTypeClass.getName();
    }
    methodMsg += paramTypeClassStr;
    Class<?> returnTYpes = method.getReturnType();
    methodMsg += returnTYpes.getName();

    return methodMsg;
  }
  @Override
  public Object annotationTargetHandle(String annotationName, MethodInvocation invocation)
      throws Throwable {
    AnnotationStrategy annotationStrategy = annotationStrategyMap.get(annotationName);
    return annotationStrategy.annotationTargetHandle(annotationName, invocation);
  }
}
